/**
 * 夹板 JB 众赢投顾 主图指标
 */

import {movingAverage as _average} from './MA'
import * as zytgTools from './ZYTG_UTIL'

const JB_A_UP_MIN = 17    //A上涨确认幅度
const JB_B_DOWN_MIN = 15   //B下跌最小确认幅度
const JB_B_DOWN_MAX = 0.65   //B下跌最大确认幅度
const JB_UP_BIAS_SIZE = 1.05      //夹板有效价格上限
const JB_DOWN_BIAS_SIZE = 0.9     //夹板有效价格下限
const JB_MIN_DAYS_TO_NOW = 45     //A类点距离现在的最短天数
const JB_MIN_DAYS_TO_NOW2 = 60     //A类点距离现在的最短天数2
const JB_DAYS_TO_ISSUE = 240     //B点距离发行日
const JB_DAYS_PRPARE = 150     //左侧提前计算日期

let initParam = {
  name: 'JB',
  type: 'polyline',
  dataAppendCount: 400,  // 默认用
}

/**
 * Caculator 数据处理
 *
 */
/**
 * Caculator 数据处理
 * @param  {array} rawData
 * @return {array} 一维数组
 *
 *
 * 数据结构说明
[
  {
      LX: 'A点前低点坐标',
      LTime: 'A点前低点时间',
      AX: 'A点x坐标',
      ATime: 'A点时间',
      BX: 'B点x坐标',
      BTime: 'B点时间',
      noNewA: 'B后是否新A'
      CX: '首次反弹点x坐标',
      CTime: '首次反弹点时间',
      startX: 86,
      endX: 90,
      startTime: 2017-07-11,
      endTime: 2017-07-16,
      startY: 86,
      endY: 90,
      color: 'blue',
      format: 'rect'
  }
]
 */
function caculate (rawData, dataSliceCount, endIndex, summary) {
  if (!(rawData instanceof Array)) throw new Error('Data or caculateParam is not Array')
  let mathData = _caculator(rawData, dataSliceCount, endIndex, summary)
  return mathData
}

function _caculator (rawData, dataSliceCount, endIndex, summary) {
  let dataPoints = []

  let maxHigh //每段累计高价
  let minLow  //每段累计低价
  let L0Low   //A开始45天内最低价
  let begin   //夹板起算低点（A之前）
  let section //夹板区域
  let pendingK //待确认K线
  let progress = -1 //查找进展(-1：初始化；0:查找A; 1:待定A；2：待定B；3：确定section；4：section可以绘图)
  let hasNewProgress
  let L0Ready = false //L0是否有效
  let previousSection

  let maArray = zytgTools.calcMAByFields(rawData, [
    { field: "close", period: "240" }
  ])

  let startI = dataSliceCount > JB_DAYS_PRPARE ? dataSliceCount - JB_DAYS_PRPARE : 0
  for (let i = startI; i < rawData.length; i++) {
    let today = rawData[i]
    let totalUp = 0   //最近涨幅
    let totalDown = 0 //最近跌幅

    if ((i > endIndex) && (progress < 2)) {
      break
    }
    hasNewProgress = false //本次循环是否发生progress变化
    L0Ready = L0Low ? i > L0Low.worseIndex : false

    if (progress == -1) {
      begin = findLastMin(i) //查找最靠近当前点的上一个低点
      minLow = rawData[begin].low
      maxHigh = rawData[begin].high
      progress = 0 //查找第一个A
      L0Ready = false
      L0Low = null
      if (previousSection) previousSection = null //重新开始新的夹板搜索则不再考虑上一A点
    }
    //画线条件：A到现在的距离大于45
    if ((progress < 3) && (rawData.length - i <= JB_MIN_DAYS_TO_NOW)) {
      break
    }

    if (progress == 0 || progress == 2 ) {
      totalUp = (today.high - minLow) * 100 / minLow
      totalDown = (maxHigh - today.low) * 100 / maxHigh
      if (pendingK && rawData[pendingK].low > today.low) {
        pendingK = i
      }
    } else if (progress == 1) {
      totalDown = (maxHigh - today.low) * 100 / maxHigh
      totalUp = (today.high - minLow) * 100 / minLow
      if (pendingK && rawData[pendingK].high < today.high) {
        pendingK = i
      }
    }
    zytgTools.debug("calc", i, today.time, "tlow", "low", today.low, "min", minLow, "down", totalDown, "high", today.high, "max", maxHigh, "up", totalUp)

    let downMeetMin = totalDown >= JB_B_DOWN_MIN
    let upMeetMin = totalUp >= JB_A_UP_MIN
    zytgTools.debug("prog", progress, "downM", downMeetMin, "upM", upMeetMin, "begin", begin)

    //出现下跌15%+
    if (downMeetMin) {
      //待确认A点，下跌过程
      if (progress == 1) {
        //在Ha到L2过程中，出现15%的回落, 且不存在17%反弹则确认A点成立
        if ((today.high < maxHigh) || !upMeetMin) {
          section = {
            AX: pendingK,
            ATime: rawData[pendingK].time,
            startX: pendingK,
            startY: rawData[pendingK].high
          }
          minLow = today.low
          maxHigh = today.high
          pendingK = i
          changeProgress(2)
        }
        zytgTools.debug("A confirmed, section init", section)
      } else if (progress == 0) {
        // zytgTools.debug("cancel A")
        //在L1到Ha过程中，没有出现15%的回落
        changeProgress(-1)
        continue
      }
    }

    //出现上涨17%+
    if (upMeetMin) {
      //待确认A点，下跌过程
      // if (progress == 1) {
      //   //在Ha到L2过程中，出现15%的回落, 且不存在17%反弹则确认A点成立
      //   zytgTools.debug("cancel pending A")
      //   changeProgress(0)
      // }
      //待查找A点，上涨过程
      if (progress == 0) {
        //满足L1*1.17<A的高点Ha 待确认
        if (!downMeetMin) {
          changeProgress(1)
          pendingK = i
          if (i > 0 && rawData[i - 1].high > today.high) {
            pendingK = i - 1 //用于处理连续两个夹板，前一个夹板结束k线的预算，导致后一个夹板A点计算迟了一根k线的情况
          }
          maxHigh = today.high
          minLow = today.low
          zytgTools.debug("pendingA", i, today)
        } 
      } 

      //待确定B点，上涨过程
      if (progress == 2) {
        //A之后的低点反弹17%，低点所在的K线即为B
        //默认取45天内最低点，如果超过最低限制则夹板不成立
        let endDay = section.AX + JB_MIN_DAYS_TO_NOW - 1
        let cancelB = false
        L0Low = zytgTools.findMaxMinOfDays(rawData, endDay, JB_MIN_DAYS_TO_NOW, zytgTools.MMD_GET_ALL, 'low')
        //A以来的最低点L0（A到现在45天内的最低点）>Ha*0.65 
        if (L0Low.worse < rawData[section.AX].high * JB_B_DOWN_MAX) {
          // zytgTools.debug("cancel A+B by L0")
          cancelB = true
        }
        //如果Lb1<=ma(c,240)或A上一B离上市首日天数小于240日时,需满足L0>Lb1
        if (previousSection && previousSection.needComparePreB) {
          if (rawData[previousSection.BX].low >= today.low || L0Low.worse <= rawData[previousSection.BX].low) {
            zytgTools.debug("cancel A+B by previous", previousSection)
            cancelB = true
          }
        }
        if (cancelB) {
          changeProgress(-1)
          continue
        }
        section = Object.assign({}, section, {
            BX: pendingK,
            BTime: rawData[pendingK].time,
            CX: i,
            CTime: today.time
        })
        changeProgress(3)
        zytgTools.debug("B confirmed, section ready", section)
      }
    }

    //判断夹板可否进行画线
    if (progress == 3) {
      //A以来的最高价 不能高于 A的最高价Ha*1.05
      let overHigh = today.high > section.startY * JB_UP_BIAS_SIZE
      //A以来的最低点L0 不能低于 L0*0.9
      let overLow = today.low < L0Low.worse * JB_DOWN_BIAS_SIZE
      let isLastDay = (i == rawData.length - 1)
      //夹板未结束，也没到当前最后一天
      if (!overHigh && !overLow && !isLastDay) {
        continue
      }

      let canDraw = (overHigh || overLow || isLastDay) && checkCanDraw(i)

      if (!canDraw) {
        if (overHigh) {
          let isNewA = rawData.length - i > JB_MIN_DAYS_TO_NOW

          if (isNewA) {
            //当A上一B低点Lb1<=ma(c,240)或者A上一B离上市首日天数<=240日时，A之后第一个B的低点Lb要小于Lb1;
            //当A上一B离上市首日天数>240并且Lb1>ma(c,240)时,Lb不与Lb1比较
            let needComparePreB = JB_DAYS_TO_ISSUE >= zytgTools.calcPastDays(summary.issue_date, section.BTime)
            needComparePreB = needComparePreB || rawData[section.BX].low <= maArray.close240[section.BX]
            let pendingAStart = getPendingStart(i);
            if (pendingAStart) {
              previousSection = section
              previousSection.needComparePreB = needComparePreB
              section = null
              begin = pendingAStart
              changeProgress(1)
              pendingK = i
              maxHigh = today.high
              // zytgTools.debug("pendingA by B", i, today)
              continue
            }
          } 
          zytgTools.debug("over 1.05 cancel section", section)
          section = null
          changeProgress(-1)
        }

        if (overLow) {
          section = null
          changeProgress(-1)
        }
      }

      if (canDraw) {
        changeProgress(4)
        let quitPoint = isLastDay ? rawData[i] : rawData[i - 1]
        section = Object.assign({}, section, {
            endX: isLastDay ? i : i - 1,
            startTime: section.ATime,
            endTime: quitPoint.time,
            endY: L0Low.worse,
            color: 'blue',
            format: 'rect'
        })
        zytgTools.debug("done", quitPoint.endX > dataSliceCount, section)
        if (section.endX >= dataSliceCount && section.startX < endIndex) {
          dataPoints.push(section)
        }
        findNext()
      }
    }

    if (progress == 0) {
      //发现更低的低点，用作起步点
      if (today.low < minLow) {
        begin = i
      }
    }
    if (today.low < minLow || hasNewProgress) {
      minLow = today.low
    } 
    if (today.high > maxHigh || hasNewProgress) {
      maxHigh = today.high
    } 

  }

  for (let j = 0; j < dataPoints.length; j++) {
    dataPoints[j].startX -= dataSliceCount
    dataPoints[j].endX -= dataSliceCount
    dataPoints[j].AX -= dataSliceCount
    dataPoints[j].BX -= dataSliceCount
    dataPoints[j].CX -= dataSliceCount
  }
  // zytgTools.debug(JSON.stringify(dataPoints))
  return dataPoints

  function checkCanDraw(i){
    let today = rawData[i]
    //如果A之后45内的最低点L0，之后到45天时最大反弹高度超过了17%，则取L0为夹板下沿；如果没有超过17%，则取A之后的60天的最低点为夹板下沿。
    let maxHighAfterL0 = 0
    if (L0Ready) {
      maxHighAfterL0 = zytgTools.findMaxMinOfDays(rawData, i, i - L0Low.worseIndex, zytgTools.MMD_GET_MAX, 'high')
      L0Ready = (maxHighAfterL0 - L0Low.worse) * 100 / L0Low.worse >= JB_A_UP_MIN
      let endDay = section.AX + JB_MIN_DAYS_TO_NOW2 - 1
      if (!L0Ready && endDay < rawData.length) {
        L0Low = zytgTools.findMaxMinOfDays(rawData, endDay, JB_MIN_DAYS_TO_NOW2, zytgTools.MMD_GET_ALL, 'low')
        return false
      }
    } else {
      return false
    }

    //L0后的价格曾涨过(0.6*Ha+0.4*L0)的价格，即反弹至夹板高度的60%
    let canDraw = L0Ready && (i - section.AX >= JB_MIN_DAYS_TO_NOW) 
    return canDraw && section.startY * 0.6 + L0Low.worse * 0.4 <= maxHighAfterL0
  }

  function changeProgress(newProgress) {
    hasNewProgress = true
    progress = newProgress
  }

  function findNext() {
    section = null
    L0Low = null
    progress = -1
    hasNewProgress = false
    L0Ready = false
    previousSection = null
    pendingK = null
  }

  function getPendingStart(aIndex) {
    let tmpLow = rawData[aIndex].low
    let minPoint = aIndex
    let start = 0
    for (let j = aIndex; j >= section.BX; j--) {
      let current = rawData[j]
      if ((current.high - tmpLow) * 100 / current.high > JB_B_DOWN_MIN) {
        start = minPoint
        break
      }
      if (current.low < tmpLow) {
        tmpLow = current.low
        minPoint = j
      }
    }
    if (start == 0) {
      start = section.BX
    }
    tmpLow = rawData[start].low
    return ((rawData[aIndex].high - tmpLow) * 100 / tmpLow >= JB_A_UP_MIN) ? start : 0
  }

  function findLastMin(current) {
    let tmpLow = rawData[current].low
    let minPoint = current
    let start = 0
    for (let j = current; j >= 0; j--) {
      let current = rawData[j]
      if ((current.high - tmpLow) * 100 / current.high > JB_B_DOWN_MIN) {
        start = minPoint
        break
      }
      if (current.low < tmpLow) {
        tmpLow = current.low
        minPoint = j
      }
    }
    return start < dataSliceCount - JB_DAYS_PRPARE ? 0 : start
  }
}


export default {
  initParam: initParam,
  caculate: caculate
}
